package com.easyticket.common.dao.impl;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

import com.easyticket.common.dao.EntityDao;

/**
 * Hibernate的范型基类.
 * 
 * 参考SpringSide源代码实现,可以在service类中直接创建使用.也可以继承出DAO子类,在多个Service类中共享DAO操作.
 * 参考Spring2.5自带的Petlinc例子,取消了HibernateTemplate.
 * 通过Hibernate的sessionFactory.getCurrentSession()获得session,直接使用Hibernate原生API.
 *
 * @param <T>  DAO操作的对象类型
 * @param <PK> 主键类型
 * 
 * @author lmy
 */
@SuppressWarnings("unchecked")
@Repository("entityDao")
@Scope("singleton")
public class HibernateGenericDaoImpl<T, PK extends Serializable> implements EntityDao<T, PK> {

    protected Logger log = LoggerFactory.getLogger(getClass());
    
    @Resource(name = "sessionFactory")
    protected SessionFactory sessionFactory;

    protected Class<T> entityClass;
    
    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    
    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    public void setSessionFactory(final SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    
    /****************************************find method *******************************************/
    /**
     * 按id获取对象.
     */
    public T load(final Class<T> entityClass, final PK id) {
        Assert.notNull(id, "id不能为空");
        return (T)getSession().load(entityClass, id);
        
    }
    
    /**
     * 取得对象的主键名.
    */ 
    public String getIdName(final Class<T> entityClass) {
        ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
        Assert.notNull(meta, "Class " + entityClass.getSimpleName() + " not define in HibernateSessionFactory.");
        return meta.getIdentifierPropertyName();
    }
    
    /**
     * 按Criteria查询对象列表.
     * 
     * @param criterions 数量可变的Criterion.
     */
    public List<T> find(final Criterion... criterions) {
        return createCriteria(criterions).list();
    }
    
    /**
     *  获取全部对象.
     */
    public List<T> getAll() {
        return find();
    }
    
    /**
     * 按属性查找对象列表,匹配方式为相等.
     */
    public List<T> findBy(final String propertyName, final Object value) {
        Assert.hasText(propertyName, "propertyName不能为空");
        Criterion criterion = Restrictions.eq(propertyName, value);
        return find(criterion);
    }

    /**
     * 按属性查找唯一对象,匹配方式为相等.
     */
    public T findByUnique(final String propertyName, final Object value) {
        Assert.hasText(propertyName, "propertyName不能为空");
        Criterion criterion = Restrictions.eq(propertyName, value);
        return (T) createCriteria(criterion).uniqueResult();
    }

    /**
     * 按HQL查询对象列表.
     * 
     * @param values 数量可变的参数,按顺序绑定.
     */
    public List<T> find(final String hql, final Object... values) {
        return createQuery(hql, values).list();
    }

    /**
     * 按HQL查询对象列表.
     * 
     * @param values 命名参数,按名称绑定.
     */
    public List<T> find(final String hql, final Map<String, Object> values) {
        return createQuery(hql, values).list();
    }

    /**
     * 按HQL查询唯一对象.
     * 
     * @param values 数量可变的参数,按顺序绑定.
     */
    public T findUnique(final String hql, final Object... values) {
        return (T) createQuery(hql, values).uniqueResult();
    }

    /**
     * 按HQL查询唯一对象.
     * 
     * @param values 命名参数,按名称绑定.
     */
    public T findUnique(final String hql, final Map<String, Object> values) {
        return (T) createQuery(hql, values).uniqueResult();
    }

    /**
     * 按HQL查询Integer类型结果.
     */
    public Integer findInt(final String hql, final Object... values) {
        return (Integer) findUnique(hql, values);
    }

    public Integer findInt(final String hql, final Map<String, Object> values) {
        return (Integer) findUnique(hql, values);
    }

    /**
     * 按HQL查询Long类型结果.
     */
    public Long findLong(final String hql, final Object... values) {
        return (Long) findUnique(hql, values);
    }

    /**
     * 按HQL查询Long类型结果.
     */
    public Long findLong(final String hql, final Map<String, Object> values) {
        return (Long) findUnique(hql, values);
    }
    
    /**
     * 根据查询HQL与参数列表创建Query对象.
     * 
     * 本类封装的find()函数全部默认返回对象类型为T,当不为T时使用本函数.
     * 
     * @param values 数量可变的参数,按顺序绑定.
     */
    public Query createQuery(final String queryString, final Object... values) {
        Assert.hasText(queryString, "queryString不能为空");
        Query query = getSession().createQuery(queryString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query;
    }

    /**
     * 根据查询HQL与参数列表创建Query对象.
     * 
     * @param values 命名参数,按名称绑定.
     */
    public Query createQuery(final String queryString, final Map<String, Object> values) {
        Assert.hasText(queryString, "queryString不能为空");
        Query query = getSession().createQuery(queryString);
        if (values != null) {
            query.setProperties(values);
        }
        return query;
    }

    /**
     * 根据Criterion条件创建Criteria.
     * 
     * 本类封装的find()函数全部默认返回对象类型为T,当不为T时使用本函数.
     * 
     * @param criterions 数量可变的Criterion.
    */ 
    public Criteria createCriteria(final Criterion... criterions) {
        Criteria criteria = getSession().createCriteria(entityClass);
        for (Criterion c : criterions) {
            criteria.add(c);
        }
        return criteria;
    }
    
    
    /**
     * 按Criteria查询唯一对象.
     * 
     * @param criterions 数量可变的Criterion.
    */
    public T findUnique(final Criterion... criterions) {
        return (T) createCriteria(criterions).uniqueResult();
    }
	
    /****************************************find method end****************************************/
    
    /***************************************save method ********************************************/
    /**
     * 保存新增或修改的对象.
     */
    public void save(T entity) {
        Assert.notNull(entity);
        getSession().persist(entity);
        log.info("save entity: {}", entity);
    }
    /***************************************save method end****************************************/
    
    /***************************************delete method ********************************************/
    /**
     * 删除对象.
     * 
     * @param entity 对象必须是session中的对象或含id属性的transient对象.
    */
    public void delete(final T entity) {
        Assert.notNull(entity, "entity不能为空");
        getSession().delete(entity);
        //log.debug("delete entity: {}", entity);
    }
     
    /**
     * 按id删除对象.
    */ 
    public void delete(final Class<T> entityClass, final PK id) {
        Assert.notNull(id, "id不能为空");
        delete(load(entityClass, id));
        //log.debug("delete entity {},id is {}", entityClass.getSimpleName(), id);
    }
    
    /***************************************delete method end*****************************************/

    /****************************************update method ******************************************/
    /**
     * @param entity 对象必须是session中的对象或含id属性的transient对象.
     */
    public void update(final T entity) {
    	getSession().update(entity);
    }
    
    /**
     * 按id更新对象.
    */
    public void update(final Class<T> entity, final PK id) {
    	update(load(entity, id));
    }
    /****************************************update method end***************************************/
    
    /****************************************batch method  ******************************************/
    /**
     * 执行HQL进行批量修改/删除操作.
     */
    public int batchExecute(final String hql, final Object... values) {
        return createQuery(hql, values).executeUpdate();
    }

    /**
     * 执行HQL进行批量修改/删除操作.
     */
    public int batchExecute(final String hql, final Map<String, Object> values) {
        return createQuery(hql, values).executeUpdate();
    }
    
    
    /****************************************batch method  end***************************************/
    
    /**
     * 按HQL分页查询.
     * @param page     页面对象
     * @param hql      HQL语句
     * @param values   可变参数列表
     * @return         分页数据
     
    public Page<T> findByPage(final Page<T> page, final String hql,
            final Object... values) {
        try {
            if (log.isDebugEnabled()) {
                log.debug("开始查找指定HQL分页数据," + hql);
            }
            return (Page<T>) getHibernateTemplate().execute(
                    new HibernateCallback() {
                        public Object doInHibernate(Session s)
                                throws HibernateException, SQLException {
                            Query query = createQuery(s, hql, values);
                            if (page.isFirstSetted()) {
                                query.setFirstResult(page.getFirst());
                            }
                            if (page.isPageSizeSetted()) {
                                query.setMaxResults(page.getPageSize());
                            }
                            page.setResult(query.list());
                            if (logger.isDebugEnabled()) {
                                logger.debug("查找指定HQL分页数据成功," + hql);
                            }
                            return page;
                        }
                    });
        } catch (RuntimeException e) {
            log.error("分页查询异常，HQL：" + hql, e);
            e.printStackTrace();
        }
    }
    */
}